﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using GameLibrary;

namespace UserInterface.Helpers.Graph
{
    [Serializable]
    public class Graph
    {
        private Dictionary<GraphNode, HashSet<GraphNode>> AdjacencyList;

        public int GraphCount
        {
            get { return this.AdjacencyList.Count; }
        }

        public IEnumerable<GraphNode> GraphNodes
        {
            get { return this.AdjacencyList.Keys; }
        }

        public IEnumerable<GraphNode> GetNeighborsOfOneNode(GraphNode node)
        {
            return this.AdjacencyList[node];
        }

        public Graph()
        {
            this.AdjacencyList = new Dictionary<GraphNode, HashSet<GraphNode>>();
        }

        /// <summary>
        /// Only add node to graph
        /// </summary>
        /// <param name="node"></param>
        public void AddNode(GraphNode node)
        {
            if (this.AdjacencyList.ContainsKey(node) == false)
                this.AdjacencyList.Add(node,new HashSet<GraphNode>());
        }

        /// <summary>
        /// Add edges between node and neighbors to graph 
        /// and if neighbors not exist they will be added to graph.
        /// </summary>
        /// <param name="node"></param>
        /// <param name="neighbors"></param>
        public void AddNeighborsOfOneNode(GraphNode node, IEnumerable<GraphNode> neighbors)
        {
            foreach (GraphNode neighbor in neighbors)
            {
                this.AddNode(neighbor);
                this.AddEdge(node, neighbor);
            }
        }

        /// <summary>
        /// Add node a and b to each's neighbor list.
        /// </summary>
        /// <param name="NodeA"></param>
        /// <param name="NodeB"></param>
        private void AddEdge(GraphNode NodeA, GraphNode NodeB)
        {
            this.AdjacencyList[NodeA].Add(NodeB);
            this.AdjacencyList[NodeB].Add(NodeA);
        }

        /// <summary>
        /// Remove Node and its edges from graph
        /// </summary>
        public void RemoveNode(GraphNode node)
        {
            this.RemoveNodeFromNeighborListOfItsNeighbors(node, this.AdjacencyList[node]);
            this.AdjacencyList.Remove(node);
        }

        private void RemoveNodeFromNeighborListOfItsNeighbors(GraphNode node, IEnumerable<GraphNode> neighbors)
        {
            foreach (GraphNode neighbor in neighbors)
                this.AdjacencyList[neighbor].Remove(node);
        }
    }
}
